import { memo, useRef, useState, useEffect, forwardRef, useCallback, type ChangeEvent } from 'react';
import { createPortal } from 'react-dom';
import { message, Popconfirm } from 'antd';
import Button from '@/components/Button';
import { preventDefault } from '@/common/utils';
import type { Props, FileHandle } from './interface';

import styles from './index.module.scss';

/** 支持的文件类型 */
const accept = ['.css', '.scss', '.less'];
/** 文件选择器实例 */
let fileHandle: FileHandle;

/**
 * 文件选择上传和拖拽上传
 */
function Upload(props: Props, ref) {
  const { saveContainer, originalValue, modifiedValue, onChange } = props;

  // 选择的文件名称
  const [fileName, setFileName] = useState('');
  // 保存是否可见
  const [visibility, setVisibility] = useState(false);
  // 容器
  const [container, setContainer] = useState<HTMLDivElement>();

  /** 文件选择器实例 */
  const inputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    // 如果编辑器内容为空，则判定为切换文件编辑
    if (!modifiedValue?.trim()) {
      setFileName('');
      setVisibility(false);
    }
  }, [modifiedValue]);

  useEffect(() => {
    const dom = saveContainer.current;
    if (dom) setContainer(dom);

    document.addEventListener('drop', handleDrop);
    document.addEventListener('dragover', preventDefault);

    return () => {
      document.removeEventListener('drop', handleDrop);
      document.removeEventListener('dragover', preventDefault);
    };
  }, []);

  /**
   * 设置文件内容
   */
  const setContent = useCallback((file: File, content: string) => {
    if (content) {
      onChange(content);
      setFileName(file.name);
    } else {
      message.error('文件内容为空');
    }
  }, []);

  /**
   * 解析文件内容
   */
  const handleFile = useCallback((file: File | undefined) => {
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (e) => setContent(file, e.target?.result as string);
    reader.readAsText(file);
  }, []);

  /**
   * 设置文件写入环境
   */
  const setFileWrite = useCallback((file: FileHandle) => {
    fileHandle = file;
    setVisibility(true);
  }, []);

  /**
   * 处理拖拽文件
   */
  const handleDropFiles = useCallback(async (files: DataTransferItemList | FileList, support: boolean) => {
    if (!files || !files.length) return;
    if (files.length > 1) {
      message.error('一次只能解析一个文件');
      return;
    }
    let file: File;
    let entry: FileSystemHandle | null = null;
    if (support) {
      const currentFile = files[0] as DataTransferItem;
      entry = await currentFile.getAsFileSystemHandle();
      if (entry.kind !== 'file') return;
      file = await entry.getFile();
    } else {
      file = files[0] as File;
    }
    if (!/\.css$|\.scss$|\.less$/.test(file.name)) {
      message.error('文件格式错误');
      return;
    }
    if (entry) setFileWrite(entry);
    handleFile(file);
  }, []);

  /**
   * 处理拖拽
   */
  const handleDrop = useCallback((e: DragEvent) => {
    preventDefault(e);
    const { dataTransfer } = e;
    if (!dataTransfer) return;
    const { items, files } = dataTransfer;
    // 判断当前环境是否支持 getAsFileSystemHandle API
    'getAsFileSystemHandle' in items[0] ? handleDropFiles(items, true) : handleDropFiles(files, false);
  }, []);

  /**
   * 处理文件
   */
  const handleChange = useCallback((e: ChangeEvent<HTMLInputElement>) => handleFile(e.target.files?.[0]), []);

  /**
   * 上传文件
   */
  const handleClick = useCallback(async () => {
    try {
      const files = await window.showOpenFilePicker({ types: [{ accept: { 'text/css': accept } }] });
      const entry = files[0];
      setFileWrite(entry);
      const file = await entry.getFile();
      handleFile(file);
    } catch (e) {
      // 排除取消操作
      if (e.name !== 'AbortError') inputRef.current?.click();
    }
  }, []);

  /**
   * 写入文件
   */
  const fileWrite = useCallback(async () => {
    try {
      if (fileHandle) {
        const writable = await fileHandle.createWritable();
        await writable.write(`${modifiedValue}\n${originalValue}`);
        await writable.close();
        message.success('保存成功');
      }
    } catch (e) {
      message.error('保存失败');
    }
  }, [originalValue, modifiedValue]);

  return (
    <>
      {container &&
        createPortal(
          <Popconfirm
            title={`是否将转换后的样式添加到 ${fileName} 中？`}
            okText="确认"
            okType="text"
            color="#252526"
            icon={null}
            arrow={false}
            showCancel={false}
            onConfirm={fileWrite}
            overlayInnerStyle={{ border: '1px solid #454545', fontSize: '13px', lineHeight: '130%' }}
          >
            <Button x-class={[styles.save, { [styles.visible]: visibility }]}>写入文件</Button>
          </Popconfirm>,
          container,
        )}
      <Button ref={ref} onClick={handleClick}>
        {fileName || '上传文件'}
      </Button>
      <input className={styles.file} ref={inputRef} type="file" accept={accept.join(',')} onChange={handleChange} />
    </>
  );
}

/**
 * 文件选择上传和拖拽上传
 */
export default memo(forwardRef(Upload));
